package de.core;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferStrategy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import de.core.gameplay.level.Level;

public class GameThread implements Runnable {
    private final static int TICKS_PER_SECOND = 25;
    private final static int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
    private final static int MAX_FRAMESKIP = 50;

    private final ExecutorService executorService;
    private final BufferStrategy bufferStrategy;

    private long nextGameTick;
    private boolean shouldExit;

    private Level currentLevel;


    public GameThread(BufferStrategy aBufferStrategy) {
        executorService = Executors.newFixedThreadPool(1);
        bufferStrategy = aBufferStrategy;
        nextGameTick = System.currentTimeMillis();
    }

    @Override
    public void run() {
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

        while (!shouldExit) {
            int gameTicks = 0;
            while (System.currentTimeMillis() > nextGameTick && gameTicks < MAX_FRAMESKIP) {
                updateGameState();

                nextGameTick += SKIP_TICKS;
                gameTicks++;
            }

            float interpolation = (System.currentTimeMillis() + SKIP_TICKS - nextGameTick) / SKIP_TICKS;
            drawScene(interpolation);
        }
        shutdown();
    }

    private void updateGameState() {
        if (currentLevel == null) {
            throw new IllegalStateException("Failed to update GameState, there is currently no Level set!");
        }
        currentLevel.updateGameState();
    }

    private void drawScene(float aInterpolation) {
        if (currentLevel == null) {
            throw new IllegalStateException("Cannot draw Scene without Level!");
        }
        Graphics2D drawGraphics = (Graphics2D) bufferStrategy.getDrawGraphics();

        drawGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        drawGraphics.setRenderingHint(RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);
        drawGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
            RenderingHints.VALUE_COLOR_RENDER_SPEED);
        drawGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BICUBIC);

        currentLevel.render(drawGraphics, aInterpolation);

        drawGraphics.dispose();
        bufferStrategy.show();
    }

    public void start() {
        shouldExit = false;
        executorService.execute(this);
    }

    private void shutdown() {
        executorService.shutdown();
    }

    public void stop() {
        shouldExit = true;
    }

    public void setCurrentLevel(Level aLevel) {
        currentLevel = aLevel;
    }
}
